package com.example.emb.service.common;

import com.hankcs.hanlp.seg.Segment;
import com.hankcs.hanlp.seg.common.Term;
import org.apache.commons.text.similarity.CosineSimilarity;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 余弦算法计算作业的重复率
 */

@Service
public class CosSimilarity implements CosSimilarityImpl{

    // todo 列出查重相似度超出的文件
    private final Segment segment;

    public CosSimilarity(Segment segment) {
        this.segment = segment;
    }

    public double calculateCosineSimilarity(String text1, List<Map<CharSequence, Integer>> mapList){
        CosineSimilarity cosineSimilarity = new CosineSimilarity();
        Double rate=0.0;
        Map<CharSequence,Integer> map0=getTermFrequencyMap(text1);
        //对所有的取平均值
        for(Map<CharSequence,Integer> mapX : mapList){
            rate+=cosineSimilarity.cosineSimilarity(map0,mapX);
        }
        //除去自己的那部分
        return (rate-1)/(mapList.size()-1);
    }


    //为什么这里一定要有private static
    public List<Map<CharSequence,Integer>> getAllTermFrequencyMap(List<String> list){
        List<Map<CharSequence,Integer>> mapList=new ArrayList<>();
        //list元素之间不引用，不互相干扰，所以使用异步的方式提高速度，但是会占用很多资源
        list.parallelStream().forEach(item->{mapList.add(getTermFrequencyMap(item));});
        return mapList;
    }
    //done
    //这个函数需要做修改，我需要改掉他的单词划分
    //去掉http的头，还有md的格式用语，不然的话问题很大
    public  Map<CharSequence, Integer> getTermFrequencyMap(String text) {
        Map<CharSequence, Integer> frequencyMap = new HashMap<>();
        //这里有问题，应该选择一个合适的分词器，不是使用简单的回车分词
        //使用Hanlp分词器
        text=moveMDTypeWords(text);
        List<Term> list=segment.seg(text);
        for (Term term:list) {
            if(!isPunctuation(term.word))
            frequencyMap.put(term.word, frequencyMap.getOrDefault(term.word, 0) + 1);
        }
        return frequencyMap;
    }
    //忽略标点符号造成的影响
    private boolean isPunctuation(CharSequence word) {
        return word.length() == 1 && !Character.isLetterOrDigit(word.charAt(0));
    }

    //去除格式符号对查重的影响
    public String  moveMDTypeWords(String markdownText){
        Stack<Integer> stack = new Stack<>();
        StringBuilder sb = new StringBuilder();
        boolean inCodeBlock = false;
        boolean inLink = false;
        boolean inHeader = false;

        for (int i = 0; i < markdownText.length(); i++) {
            char c = markdownText.charAt(i);

            // 判断是否在代码块中
            if (c == '`') {
                inCodeBlock = !inCodeBlock;
            }

            // 判断是否在链接中
            if (c == '[') {
                inLink = true;
            } else if (c == ']') {
                inLink = false;
            }

            // 判断是否在标题中
            if (c == '#' && (i == 0 || markdownText.charAt(i - 1) == '\n')) {
                inHeader = true;
            } else if (c == '\n') {
                inHeader = false;
            }

            if (c == '*' || c == '_') {
                if (!stack.isEmpty() && stack.peek() == i - 1 && !inCodeBlock && !inLink && !inHeader) {
                    // 出栈
                    stack.pop();
                    sb.append(markdownText.substring(stack.pop() + 1, i));
                } else {
                    // 入栈
                    stack.push(i);
                }
            } else {
                if (stack.isEmpty() && !inCodeBlock && !inLink && !inHeader) {
                    sb.append(c);
                }
            }
        }

        return sb.toString();
    }
}
